home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / expecTerm / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-17  |  20.6 KB  |  819 lines  |  [TEXT/MPS ]

  1. /* main.c - main() and some logging routines for expect
  2. ***************************************************************************** 
  3. expecTerm version 1.0 beta
  4. Mark Weissman
  5. Christopher Matheus
  6. Copyright 1992 by GTE Laboratories Incorporated.
  7.  
  8. Portions of this work are in the public domain.  Permission to use,
  9. copy, modify, and distribute this software and its documentation for
  10. any purpose and without fee is hereby granted, provided that the above
  11. copyright notice appear in all copies and that both the copyright
  12. notice and warranty disclaimer appear in supporting documentation, and
  13. that the names of GTE Laboratories or any of their entities not be
  14. used in advertising or publicity pertaining to distribution of the
  15. software without specific, written prior permission.
  16.  
  17. GTE disclaims all warranties with regard to this software, including
  18. all implied warranties of merchantability and fitness for a particular
  19. purpose, even if GTE Laboratories Incorporated knows about the
  20. purpose.  In no event shall GTE be liable for any special, indirect or
  21. consequential damages or any damages whatsoever resulting from loss of
  22. use, data or profits, whether in an action of contract, negligence or
  23. other tortuous action, arising out of or in connection with the use or
  24. performance of this software.
  25.  
  26. This code is based on and may include parts of Don Libes' expect code:
  27.   expect written by: Don Libes, NIST, 2/6/90
  28.   Design and implementation of expect was paid for by U.S. tax
  29.   dollars.  Therefore it is public domain.  However, the author and NIST
  30.   would appreciate credit if this program or parts of it are used.
  31. ******************************************************************************
  32.  
  33. Written by: Don Libes, NIST, 2/6/90
  34. Modified by: Mark Weissman 9/92
  35.  
  36. Design and implementation of this program was paid for by U.S. tax
  37. dollars.  Therefore it is public domain.  However, the author and NIST
  38. would appreciate credit if this program or parts of it are used.
  39. */
  40.  
  41. #include <stdio.h>
  42. #ifndef M_XENIX
  43.   /* netdb.h is not available on SCO XENIX 386, unless you bought
  44.      the TCP/IP Development Package.  <pf@artcom0.north.de> 26-Jan-92 : */
  45. #include <netdb.h>
  46. #endif
  47. #include <sys/types.h>
  48. #ifdef CRAY
  49. #include <sys/inode.h>    /* Cray 5.1 needs this, I don't know why */
  50. #endif
  51. #include <sys/file.h>
  52. #include <sys/ioctl.h>
  53. #include <signal.h>
  54. #ifndef NO_STRING_H
  55. #include <string.h>
  56. #else
  57. char *strchr();
  58. #endif
  59. #include <sys/time.h>
  60. #include <errno.h>
  61. #ifdef EXTERN_ERRNO
  62. extern int errno;
  63. #endif
  64. #include "expterm.h"
  65. #include <tcl.h>
  66. #include <tclInt.h>
  67.  
  68. #ifndef va_dcl
  69. /* unfortunately, tclInt.h includes varargs, which on some systems, namely */
  70. /* SunOS 4.0.3, does protect against reinclusion.  Do so here. */
  71. #include <varargs.h>
  72. #endif
  73. #include "global.h"
  74. #include "translate.h"
  75.  
  76. #ifdef __SABER__
  77. #undef    VERSION
  78. #define    VERSION        "Saber"
  79. #undef    SCRIPTDIR
  80. #define SCRIPTDIR    "test/"
  81. #endif
  82. static char exp_version[] = VERSION;
  83. #define VERSION_VARNAME   "expect_version"
  84. #define NEED_TCL_MAJOR        6
  85. #define NEED_TCL_MINOR        1
  86. #define SCRIPTDIR_VARNAME "expect_library"
  87.  
  88. Tcl_Interp *interp;        /* Tcl instantiation */
  89. int loguser = TRUE;        /* if TRUE, expect/spawn may write to stdout */
  90. int logfile_all = FALSE;    /* if TRUE, write log of all interactions */
  91.                 /* despite value of loguser. */
  92. FILE *logfile = 0;
  93. FILE *cmdfile = 0;
  94. FILE *debugfile = 0;
  95. int is_debugging = FALSE;
  96. void init_cmd_interpreter();
  97. void exp_init_pty();
  98. void init_interact();
  99. void init_expect();
  100. void init_spawn();
  101. void init_trap();
  102. void init_unit_random();
  103.  
  104. void
  105. usage()
  106. {
  107.     errorlog(stderr,"usage: expect [-di] [-c cmds] [[-f] cmdfile] [args]\r\n");
  108.     bye(-1);
  109. }
  110.  
  111. int is_raw = FALSE;
  112. int is_noecho = FALSE;
  113.  
  114. extern int disconnected;
  115.  
  116. exp_tty tty_current, tty_cooked;
  117. int ioctled_devtty = FALSE;
  118.  
  119. /* if set == 1, set it to raw, else unset it */
  120. void
  121. tty_raw(set)
  122. int set;
  123. {
  124.     if (set == 1) {
  125.         is_raw = TRUE;
  126. #if defined(SYSV3) || defined(POSIX)
  127.         tty_current.c_iflag = 0;
  128.         tty_current.c_oflag = 0;
  129.         tty_current.c_lflag &= ECHO; /* disable everything except echo */
  130.         tty_current.c_cc[VMIN] = 1;
  131.         tty_current.c_cc[VTIME] = 0;
  132.     } else {
  133.         tty_current.c_iflag = tty_cooked.c_iflag;
  134.         tty_current.c_oflag = tty_cooked.c_oflag;
  135.         tty_current.c_lflag = tty_cooked.c_lflag;
  136.         tty_current.c_cc[VMIN] = tty_cooked.c_cc[VMIN];
  137.         tty_current.c_cc[VTIME] = tty_cooked.c_cc[VTIME];
  138. #else
  139.         tty_current.sg_flags |= RAW;
  140.     } else {
  141.         tty_current.sg_flags = tty_cooked.sg_flags;
  142. #endif
  143.         is_raw = FALSE;
  144.     }
  145. }
  146.     
  147. void
  148. tty_echo(set)
  149. int set;
  150. {
  151.     if (set == 1) {
  152.         is_noecho = FALSE;
  153. #if defined(SYSV3) || defined(POSIX)
  154.         tty_current.c_lflag |= ECHO;
  155.     } else {
  156.         tty_current.c_lflag &= ~ECHO;
  157. #else
  158.         
  159.         tty_current.sg_flags |= ECHO;
  160.     } else {
  161.         tty_current.sg_flags &= ~ECHO;
  162. #endif
  163.         is_noecho = TRUE;
  164.     }
  165. }
  166.  
  167. /* returns 0 if nothing changed */
  168. /* if something changed, the out parameters are changed as well */
  169. int
  170. tty_raw_noecho(tty_old,was_raw,was_echo)
  171. exp_tty *tty_old;
  172. int *was_raw, *was_echo;
  173. {
  174.     if (disconnected) return(0);
  175.     if (is_raw && is_noecho) return(0);
  176.  
  177.     *tty_old = tty_current;        /* save old parameters */
  178.     *was_raw = is_raw;
  179.     *was_echo = !is_noecho;
  180.     debuglog("tty_raw_noecho: was raw = %d  echo = %d\r\n",is_raw,!is_noecho);
  181.  
  182.     tty_raw(1);
  183.     tty_echo(-1);
  184.  
  185. #ifdef POSIX
  186.      if (tcsetattr(0, TCSADRAIN, &tty_current) == -1) {
  187. #else
  188. #    ifdef SYSV3
  189.         if (ioctl(0, TCSETAW, &tty_current) == -1) {
  190. #    else
  191.     if (ioctl(0, TIOCSETP, &tty_current) == -1) {
  192. #    endif
  193. #endif
  194.         errorlog("ioctl(raw): %s\r\n",sys_errlist[errno]);
  195.         bye(-1);
  196.     }
  197.  
  198.     ioctled_devtty = TRUE;
  199.     return(1);
  200. }
  201.  
  202. /* returns 0 if nothing changed */
  203. /* if something changed, the out parameters are changed as well */
  204. int
  205. tty_cooked_echo(tty_old,was_raw,was_echo)
  206. exp_tty *tty_old;
  207. int *was_raw, *was_echo;
  208. {
  209.     if (disconnected) return(0);
  210.     if (!is_raw && !is_noecho) return(0);
  211.  
  212.     *tty_old = tty_current;        /* save old parameters */
  213.     *was_raw = is_raw;
  214.     *was_echo = !is_noecho;
  215.     debuglog("tty_cooked_echo: was raw = %d  echo = %d\r\n",is_raw,!is_noecho);
  216.  
  217.     tty_raw(-1);
  218.     tty_echo(1);
  219.  
  220. #ifdef POSIX
  221.      if (tcsetattr(0, TCSADRAIN, &tty_current) == -1) {
  222. #else
  223. #    ifdef SYSV3
  224.         if (ioctl(0, TCSETAW, &tty_current) == -1) {
  225. #    else
  226.     if (ioctl(0, TIOCSETP, &tty_current) == -1) {
  227. #    endif
  228. #endif
  229.         errorlog("ioctl(noraw): %s\r\n",sys_errlist[errno]);
  230.         bye(-1);
  231.     }
  232.     ioctled_devtty = TRUE;
  233.  
  234.     return(1);
  235. }
  236.  
  237. void
  238. tty_set(tty,raw,echo)
  239. exp_tty *tty;
  240. int raw;
  241. int echo;
  242. {
  243. #ifdef POSIX
  244.      if (tcsetattr(0, TCSADRAIN, &tty_current) == -1) {
  245. #else
  246. #    ifdef SYSV3
  247.         if (ioctl(0, TCSETAW, tty) == -1) {
  248. #    else
  249.     if (ioctl(0, TIOCSETP, tty) == -1) {
  250. #    endif
  251. #endif
  252.         errorlog("ioctl(set): %s\r\n",sys_errlist[errno]);
  253.         bye(-1);
  254.     }
  255.     is_raw = raw;
  256.     is_noecho = !echo;
  257.     tty_current = *tty;
  258.     debuglog("tty_set: raw = %d, echo = %d\r\n",is_raw,!is_noecho);
  259.     ioctled_devtty = TRUE;
  260. }    
  261.  
  262. init_tty()
  263. {
  264.     extern exp_tty exp_tty_original;
  265.  
  266.     /* save original user tty-setting in 'cooked', just in case user */
  267.     /* asks for it without earlier telling us what cooked means to them */
  268.     tty_cooked = exp_tty_original;
  269.  
  270.     /* save our current idea of the terminal settings */
  271.     tty_current = exp_tty_original;
  272. }
  273.  
  274. void generic_sighandler();
  275.  
  276. void
  277. bye(status)
  278. int status;
  279. {
  280.     extern int disconnected;
  281.     extern int forked;
  282.     extern exp_tty exp_tty_original;
  283.     static int bye_in_progress = FALSE;
  284.  
  285.     /* prevent recursion */
  286.     if (bye_in_progress) {
  287.         errorlog("bye recursed\r\n");
  288.         exit(status);
  289.     }
  290.     bye_in_progress = TRUE;
  291.  
  292.     /* called user-defined exit routine if one exists */
  293.     generic_sighandler(0);
  294.  
  295.     flush_streams();    /* need to do this before tty is reset, */
  296.     /* because formatting earlier was done sensitive to tty mode */
  297.  
  298.     if (!disconnected && !forked && isatty(0) && ioctled_devtty) 
  299.       tty_set(&exp_tty_original,0,0);
  300.  
  301.     /* all other files either don't need to be flushed or will be
  302.        implicitly closed at exit.  Spawned processes are free to continue
  303.        running, however most will shutdown after seeing EOF on stdin.
  304.        Some systems also deliver SIGHUP and other sigs to idle processes
  305.        which will blow them away if not prepared.
  306.     */
  307.     fatal(NULL); /* MDW: Tue Jun 23 08:55:51 1992 */
  308.     exit(status);
  309. }
  310.  
  311. /* Following this are several functions that log the conversation. */
  312. /* Most of them have multiple calls to printf-style functions.  */
  313. /* At first glance, it seems stupid to reformat the same arguments again */
  314. /* but we have no way of telling how long the formatted output will be */
  315. /* and hence cannot allocate a buffer to do so. */
  316. /* Fortunately, in production code, most of the duplicate reformatting */
  317. /* will be skipped, since it is due to handling errors and debugging. */
  318.  
  319. /* send to log if open */
  320. /* send to stderr if debugging enabled */
  321. /* use this for logging everything but the parent/child conversation */
  322. /* (this turns out to be almost nothing) */
  323. /* uppercase L differentiates if from math function of same name */
  324. #define LOGUSER        (loguser || force_stdout)
  325. /*VARARGS*/
  326. void
  327. Log(va_alist)
  328. va_dcl
  329. {
  330.     int force_stdout;
  331.     char *fmt;
  332.     va_list args;
  333.  
  334.     va_start(args);
  335.     force_stdout = va_arg(args,int);
  336.     fmt = va_arg(args,char *);
  337.     if (debugfile) vfprintf(debugfile,fmt,args);
  338.     if (logfile_all || (LOGUSER && logfile)) vfprintf(logfile,fmt,args);
  339.     if (LOGUSER) vfprintf(stdout,fmt,args);
  340.     va_end(args);
  341. }
  342.  
  343. /* just like log but does no formatting */
  344. /* send to log if open */
  345. /* use this function for logging the parent/child conversation */
  346. void
  347. nflog(buf,force_stdout)
  348. char *buf;
  349. int force_stdout;    /* override value of loguser */
  350. {
  351.     int length = strlen(buf);
  352.  
  353.     if (debugfile) fwrite(buf,1,length,debugfile);
  354.     if (logfile_all || (LOGUSER && logfile)) fwrite(buf,1,length,logfile);
  355.     if (LOGUSER) fwrite(buf,1,length,stdout);
  356. }
  357. #undef LOGUSER
  358.  
  359. /* send to log if open and debugging enabled */
  360. /* send to stderr if debugging enabled */
  361. /* use this function for recording unusual things in the log */
  362. /*VARARGS*/
  363. void
  364. debuglog(va_alist)
  365. va_dcl
  366. {
  367.     char *fmt;
  368.     va_list args;
  369.  
  370.     va_start(args);
  371.     fmt = va_arg(args,char *);
  372.     if (debugfile) vfprintf(debugfile,fmt,args);
  373.     if (is_debugging) {
  374.         vfprintf(stderr,fmt,args);
  375.         if (logfile) vfprintf(logfile,fmt,args);
  376.     }
  377.  
  378.     va_end(args);
  379. }
  380.  
  381. /* send to log if open */
  382. /* send to stderr */
  383. /* use this function for error conditions */
  384. /*VARARGS*/
  385. void
  386. errorlog(va_alist)
  387. va_dcl
  388. {
  389.     char *fmt;
  390.     va_list args;
  391.  
  392.     va_start(args);
  393.     fmt = va_arg(args,char *);
  394.     vfprintf(stderr,fmt,args);
  395.     if (debugfile) vfprintf(debugfile,fmt,args);
  396.     if (logfile) vfprintf(logfile,fmt,args);
  397.     va_end(args);
  398. }
  399.  
  400. /* just like errorlog but does no formatting */
  401. /* send to log if open */
  402. /* use this function for logging the parent/child conversation */
  403. /*ARGSUSED*/
  404. void
  405. nferrorlog(buf,force_stdout)
  406. char *buf;
  407. int force_stdout;    /* not used, only declared here for compat with */
  408.             /* nflog() */
  409. {
  410.     int length = strlen(buf);
  411.     fwrite(buf,1,length,stderr);
  412.     if (debugfile) fwrite(buf,1,length,debugfile);
  413.     if (logfile) fwrite(buf,1,length,logfile);
  414. }
  415.  
  416. /* user has pressed escape char from interact or somehow requested expect.
  417. If a user-supplied command returns:
  418.  
  419. TCL_ERROR,    assume user is experimenting and reprompt
  420. TCL_OK,        ditto
  421. TCL_RETURN,    return TCL_OK (assume user just wants to escape() to return)
  422. TCL_RETURN_TCL,    return TCL_RETURN
  423. anything else    return it
  424. */
  425. int
  426. escape(interp)
  427. Tcl_Interp *interp;
  428. {
  429.     int rc;
  430.     char *ccmd;        /* pointer to complete command */
  431.     char line[BUFSIZ];    /* space for partial command */
  432.     int newcmd = TRUE;
  433.     Tcl_CmdBuf buffer;
  434.     Interp *iPtr = (Interp *)interp;
  435.     int tty_changed = FALSE;
  436.  
  437.     exp_tty tty_old;
  438.     int was_raw, was_echo;
  439.  
  440.     if (!(buffer = Tcl_CreateCmdBuf())) {
  441.         Tcl_Return(interp,"no more space for cmd buffer\r\n",TCL_STATIC);
  442.         return(TCL_ERROR);
  443.     }
  444.     newcmd = TRUE;
  445.     while (TRUE) {
  446.         /* force terminal state */
  447.         tty_changed = tty_cooked_echo(&tty_old,&was_raw,&was_echo);
  448.  
  449. #if 0
  450.         if (newcmd) Log(1,"expecTerm%d.%d> ",
  451.             iPtr->numLevels,iPtr->curEvent);
  452.         else nflog("+> ",1);
  453. #endif
  454.  
  455. #define PROMPT1    "prompt1"
  456. #define PROMPT2    "prompt2"
  457.  
  458.         if (newcmd) {
  459.             rc = Tcl_Eval(interp,PROMPT1,0,(char **)0);
  460.             if (rc == TCL_OK) Log(1,"%s",interp->result);
  461.             else Log(1,"expecTerm%d.%d> ",iPtr->numLevels,
  462.                            iPtr->curEventNum+1);
  463.         } else {
  464.             rc = Tcl_Eval(interp,PROMPT2,0,(char **)0);
  465.             if (rc == TCL_OK) Log(1,"%s",interp->result);
  466.             else Log(1,"+> ",1);
  467.         }
  468.         flush_streams();
  469.  
  470.         if (fgets(line,BUFSIZ,stdin) == NULL) {
  471.           debuglog("after FGETS, newcmd: %d, line: %s", newcmd, line);
  472.           if (newcmd) {
  473.             perror("fgets");
  474.             bye(0); /* user sent EOF or disappeared */
  475.           }
  476.           line[0] = 0;
  477.         }
  478.         if (debugfile) fwrite(line,1,strlen(line),debugfile);
  479.         /* intentionally always write to logfile */
  480.         if (logfile) fwrite(line,1,strlen(line),logfile);
  481.         /* no need to write to stdout, since they will see it */
  482.         /* just from it having been echoed as they are typing it */
  483.  
  484.         if (NULL == (ccmd = Tcl_AssembleCmd(buffer,line))) {
  485.             newcmd = FALSE;
  486.             continue;    /* continue collecting command */
  487.         }
  488.         newcmd = TRUE;
  489.  
  490.         if (tty_changed) tty_set(&tty_old,was_raw,was_echo);
  491.         switch (rc = Tcl_RecordAndEval(interp,ccmd,0)) {
  492.         case TCL_OK:
  493.             if (*interp->result != 0)
  494.                 Log(0,"%s\r\n",cook(interp->result,(int *)0));
  495.             continue;
  496.         case TCL_ERROR:
  497.             if (*interp->result) {
  498.                 /* split into 2 errorlog calls, only because */
  499.                 /* cook uses a static buffer */
  500.                 /* note that ccmd has a trailing newline */
  501.                 errorlog("error in command: %s",
  502.                     cook(ccmd,(int *)0));
  503.                 errorlog("%s\r\n",
  504.                     cook(interp->result,(int *)0));
  505.             }
  506.             /* since user is typing by hand, we expect lots
  507.                of errors, and want to give another chance */
  508.             continue;
  509. #define finish(x)    {rc = x; goto done;}
  510.         case TCL_BREAK:
  511.         case TCL_CONTINUE:
  512.             finish(rc);
  513.         case TCL_RETURN_TCL:
  514.             finish(TCL_RETURN);
  515.         case TCL_RETURN:
  516.             finish(TCL_OK);
  517.         default:
  518.             /* note that ccmd has trailing newline */
  519.             errorlog("error %d: %s\r\n",rc,ccmd);
  520.             continue;
  521.         }
  522.     }
  523.     /* cannot fall thru here, must jump to label */
  524.  done:
  525.     if (tty_changed) tty_set(&tty_old,was_raw,was_echo);
  526.  
  527.     /* currently, code guarantees buffer is valid */
  528.     Tcl_DeleteCmdBuf(buffer);
  529.  
  530.     return(rc);
  531. }
  532.  
  533. static char *argv0;
  534.  
  535. static int interactive = FALSE;
  536. static int cmdlinecmds = FALSE;
  537.  
  538. init_expecTerm(argc, argv)
  539. int argc;
  540. char *argv[];
  541. {
  542.   int tcl_major = atoi(TCL_VERSION);
  543.   int tcl_minor;
  544.   char *dot = strchr(TCL_VERSION,'.');
  545.  
  546.   static char oBuf[BUFSIZ];    /* MDW: Tue Jun 23 12:03:21 1992 */
  547.   setbuf(stdout, oBuf);        /* Must be before stdout is written or read */
  548.  
  549.   tcl_minor = atoi(dot+1);
  550.  
  551.   if (tcl_major < NEED_TCL_MAJOR || tcl_minor < NEED_TCL_MINOR) {
  552.     fprintf(stderr,
  553.         "Expect compiled with Tcl %d.%d but needs at least Tcl %d.%d\n",
  554.         tcl_major,tcl_minor,NEED_TCL_MAJOR,NEED_TCL_MINOR);
  555.     exit(-1);
  556.   }
  557.  
  558.   argv0 = argv[0];
  559.  
  560.   init_cmd_interpreter();    /* Tcl interpreter */
  561.   exp_init_pty();
  562.   init_tty();            /* do this only now that pty's looked at orig tty state */
  563.  
  564.   /* initialize commands */
  565.   init_expect();
  566.   init_interact();
  567.   init_spawn();
  568.   init_trap();
  569.   init_unit_random();
  570. #ifndef USER_CUSTOMIZATION
  571. #define init_user_fun(INTERP, ARGC, ARGV) NULL
  572. #endif
  573.   init_user_fun(interp, argc, argv);
  574.  
  575.   Tcl_SetVar(interp,SCRIPTDIR_VARNAME,SCRIPTDIR,0);
  576.   Tcl_SetVar(interp,VERSION_VARNAME,VERSION,0);
  577.  
  578. }
  579.  
  580. expecTerm(argc, argv)
  581. int argc;
  582. char *argv[];
  583. {
  584.   int newcmd;
  585.   Tcl_CmdBuf buffer;
  586.   extern int optind;
  587.   extern char *optarg;
  588.   char *args;            /* ptr to string-rep of all args */
  589.  
  590. /*  
  591.   int interactive = FALSE;
  592.   int cmdlinecmds = FALSE;
  593. */
  594.   char *cmdfilename = 0;
  595.  
  596.   int c;
  597.   int rc;
  598.  
  599.   int sys_rc = TRUE;        /* read system rc file */
  600.   int my_rc = TRUE;        /* read personal rc file */
  601.  
  602.  
  603.   while ((c = getopt(argc, argv, "c:df:inN")) != EOF) {
  604.     switch(c) {
  605.     case 'c':            /* command */
  606.       cmdlinecmds = TRUE;
  607.       rc = Tcl_Eval(interp,optarg,0,(char **)0);
  608.  
  609.       if (rc != TCL_OK) {
  610.     errorlog("error in command: %s\r\n",
  611.          cook(optarg,(int *)0));
  612.     if (rc != TCL_ERROR)
  613.       errorlog("Tcl_Eval = %d\r\n",rc);
  614.     if (*interp->result != 0) {
  615.       errorlog("%s\r\n",
  616.            cook(interp->result,(int *)0));
  617.     }
  618.       }
  619.       break;
  620.     case 'd': is_debugging = TRUE;
  621.       debuglog("expect version %s\r\n",VERSION);
  622.       break;
  623.     case 'f':            /* name of cmd file */
  624.       cmdfilename = optarg;
  625.       break;
  626.     case 'i':            /* interactive */
  627.       interactive = TRUE;
  628.       break;
  629.     case 'n':            /* don't read personal rc file */
  630.       my_rc = FALSE;
  631.       break;
  632.     case 'N':            /* don't read system-wide rc file */
  633.       sys_rc = FALSE;
  634.       break;
  635.     default: usage();
  636.     }
  637.   }
  638.  
  639.   for (c = 0;c<argc;c++) {
  640.     debuglog("argv[%d] = %s  ",c,argv[c]);
  641.   }
  642.   debuglog("\r\n");
  643.  
  644.   /* get cmd file name, if we haven't got it already */
  645.   if (!cmdfilename && (optind < argc)) {
  646.     cmdfilename = argv[optind];
  647.     optind++;
  648.   }
  649.   if (cmdfilename) {
  650.     if (streq(cmdfilename,"-")) {
  651.       cmdfile = stdin;
  652.     } else if (NULL == (cmdfile = fopen(cmdfilename,"r"))) {
  653.       errorlog("%s: %s\r\n",cmdfilename,sys_errlist[errno]);
  654.       bye(-1);
  655.     }
  656.   }
  657.  
  658.   if (cmdfile && interactive) {
  659.     errorlog("cannot read commands from both file and keyboard\r\n");
  660.     bye(-1);
  661.   }
  662.  
  663.   /* collect remaining args and make into an argv */
  664.   /* Tcl expects argv[0] to be the command name, but it doesn't do */
  665.   /* anything useful with it anyway, so just back up the pointer */
  666.   /* actually, back it up twice, so we can make the resultant [0] */
  667.   /* be the program name */
  668.   /* Oh, and we add to argc for the same reason */
  669.   /* If no cmdfilename, there certainly can't be any args! */
  670.   if (cmdfilename) {
  671.     argv[optind-1] = cmdfilename;
  672.     Tcl_ListCmd((ClientData)0,interp,2+argc-optind,argv+optind-2);
  673.     args = interp->result;
  674.   } else args = "";
  675.  
  676.   debuglog("set argv {%s}\r\n",args);
  677.   Tcl_SetVar(interp,"argv",args,0);
  678.  
  679.   /* read rc files */
  680.   if (sys_rc) {
  681.     char file[200];
  682.     int fd;
  683.  
  684.     sprintf(file,"%sexpect.rc",SCRIPTDIR);
  685.     if (-1 != (fd = open(file,0))) {
  686.       if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
  687.     errorlog("error executing system initialization file: %s\r\n",file);
  688.     if (rc != TCL_ERROR) errorlog("Tcl_Eval = %d\r\n",rc);
  689.     if (*interp->result != 0) errorlog("%s\r\n",interp->result);
  690.     bye(-1);
  691.       }
  692.       close(fd);
  693.     }
  694.   }
  695.   if (my_rc) {
  696.     char file[200];
  697.     char *home;
  698.     int fd;
  699.     char *getenv();
  700.  
  701.     if (NULL != (home = getenv("HOME"))) {
  702.       sprintf(file,"%s/.expect.rc",home);
  703.       if (-1 != (fd = open(file,0))) {
  704.     if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
  705.       errorlog("error executing file: %s\r\n",file);
  706.       if (rc != TCL_ERROR) errorlog("Tcl_Eval = %d\r\n",rc);
  707.       if (*interp->result != 0) errorlog("%s\r\n",interp->result);
  708.       bye(-1);
  709.     }
  710.     close(fd);
  711.       }
  712.     }
  713.   }
  714.   /* become interactive if user requested or "nothing to do" */
  715.   if (interactive || (!cmdfile && !cmdlinecmds)) 
  716.     (void) escape(interp); 
  717.  
  718.   if (!cmdfile) bye(0);
  719.  
  720.   debuglog("executing commands from command file\r\n");
  721.  
  722.   if (!(buffer = Tcl_CreateCmdBuf())) {
  723.     errorlog("no more space for cmd buffer\r\n");
  724.     bye(0);
  725.   }
  726.   newcmd = TRUE;
  727.   while (1) {
  728.     char line[BUFSIZ];        /* buffer for partial Tcl command */
  729.     char *ccmd;            /* pointer to complete Tcl command */
  730.  
  731.     if (fgets(line,BUFSIZ,cmdfile) == NULL) {
  732.       if (newcmd) bye(0);
  733.       else line[0] = 0;
  734.     }
  735.     if (NULL == (ccmd = Tcl_AssembleCmd(buffer,line))) {
  736.       newcmd = FALSE;
  737.       continue;            /* continue collecting command */
  738.     }
  739.     newcmd = TRUE;
  740.  
  741.  
  742.     rc = Tcl_Eval(interp,ccmd,0,(char **)0);
  743.  
  744.     if (rc != TCL_OK) {
  745.       /* no \n at end, since ccmd will already have one. */
  746.       /* Actually, this is not true if command is last in */
  747.       /* file and has no newline after it, oh well */
  748.       errorlog("error in command: %s",cook(ccmd,(int *)0));
  749.       if (rc != TCL_ERROR) errorlog("Tcl_Eval = %d\r\n",rc);
  750.       if (*interp->result != 0) {
  751.     errorlog("%s\r\n",cook(interp->result,(int *)0));
  752.       }
  753.     }
  754.   }
  755.   /*NOTREACHED*/
  756. }
  757.  
  758. #include "command.h"
  759.  
  760. /*ARGSUSED*/
  761. int
  762. cmdExpectVersion(clientData, interp, argc, argv)
  763. ClientData clientData;
  764. Tcl_Interp *interp;
  765. int argc;
  766. char **argv;
  767. {
  768.     int emajor, umajor;
  769.     char *user_version;    /* user-supplied version string */
  770.  
  771.     if (argc == 1) {
  772.         Tcl_SetResult(interp,exp_version,TCL_STATIC);
  773.         return(TCL_OK);
  774.     }
  775.     if (argc > 3) {
  776.         tcl_error("usage: expect_version [[-exit] version]");
  777.         return(TCL_ERROR);
  778.     }
  779.  
  780.     user_version = argv[argc==2?1:2];
  781.     emajor = atoi(exp_version);
  782.     umajor = atoi(user_version);
  783.  
  784.     /* first check major numbers */
  785.     if (emajor > umajor) return(TCL_OK);
  786.     else if (emajor == umajor) {
  787.         int u, e;
  788.  
  789.         /* now check minor numbers */
  790.         char *dot = strchr(user_version,'.');
  791.         /* if user did not supply minor number, let it go */
  792.         if (dot) {
  793.             u = atoi(dot+1);
  794.             dot = strchr(exp_version,'.');
  795.             e = atoi(dot+1);
  796.             if (e >= u) return(TCL_OK);
  797.         }
  798.     }
  799.  
  800.     if (argc == 2) {
  801.         tcl_error("requires Expect version %s (but using %s)",
  802.             argv0,user_version,exp_version);
  803.         return(TCL_ERROR);
  804.     }
  805.     errorlog("%s: requires Expect version %s (but using %s)\r\n",
  806.         argv0,user_version,exp_version);
  807.     bye(-1);
  808.     /*NOTREACHED*/
  809. }
  810.  
  811. void
  812. main(argc, argv)
  813. int argc;
  814. char *argv[];
  815. {
  816.   init_expecTerm(argc, argv);
  817.   expecTerm(argc,argv);
  818. }
  819.